The present invention generally relates to systems for executing or otherwise performing code on one or more processors within a computerized device, and more particularly, to systems and techniques for atomically executing critical code without interference from interruptions or from other processors.
Conventional computer systems and computerized devices include one or more central processing units (CPUs) or processors that can operate to execute or otherwise perform software programs that are encoded as a series of logic instructions within a memory system accessible to the processor(s). Such computer systems also typically include an operating system program encoded within the memory system. The operating system operates as a control program that controls or governs when the processor is able to execute other programs such as user processes. Multitasking operating systems allow a single processor within a conventional computer system to execute multiple processes or threads in a back-to-back or time-sliced manner such that each process is able to move forward and make progress in its execution by utilizing a portion of processor cycles for execution. The terms process and thread will be used throughout this description interchangeably to denote a related set of logic instructions that a processor can perform (e.g., execute, interpret, run, etc.).
Some conventional computer systems include multiple processors that can operate under control of a multiprocessing operating system. Such a multiprocessing operating system controls the execution of multiple processes across the range of available processors in the computerized device. As an example of a multiprocessing computer system in operation, an operating system may begin to execute a user process on a first processor for a period of time until an interrupt of some sort occurs to that user process. Perhaps the interrupt is caused when the processor executes an instruction in the user process that requires access to data stored within a disk drive coupled to the computer system. As a result of such an input/output (I/O) request, the operating system suspends execution of the user process on the first processor while other software (e.g., an I/O process) and/or circuitry within the computer system handles any required processing associated with the I/O interrupt. When the operating system later detects that handling of the interrupt is complete and the requested data is now available for the user process, the operating system then reschedules execution of the user process on the same processor, or possibly on a second or other processor since the first processor may have already been rescheduled and may be currently executing another process. In this manner, multiprocessing operating system can xe2x80x9cmigratexe2x80x9d execution of processes from one processor to another to achieve greater overall processing throughput.
Certain software programs that execute as processes in conventional computer systems sometimes include a requirement that portions of code within the process be executed in an xe2x80x9catomicxe2x80x9d or undisturbed manner. These portions of code in a process or program are often referred to as xe2x80x9ccritical codexe2x80x9d or xe2x80x9ccritical sectionsxe2x80x9d. Generally, critical code is a series of one or more logic instructions associated with a process, such as microcode, machine language instructions, or even high-level language instructions, which a processor in the computer system must execute from start to finish without any interference. Typical sources of interference are interruptions and actions performed by other processes such as remote actions. Interference is generally defined as an external modification or change made (i.e., by code other than the critical code or the process containing the critical code) to data, memory contents, register contents, flags, or other information that is related to (e.g., referenced by) the critical code.
There are a number of reasons why a process may contain a series of instructions (i.e., critical code) that must be executed atomically (i.e., without interference). As an example, some conventional computer systems include memory systems that operate as shared memory. Shared memory may be, for example, main memory that allows two or more software processes to access the same set of memory locations during their execution. Processes can use shared memory for such functions as interprocess communication, process synchronization and for other reasons. When a process contains a series of instructions that operate on shared memory locations, it is often preferable that those instructions be executed atomically as critical code in order to ensure that the content of the shared memory is accurately maintained. If an operating system interrupts a sequence of critical code instructions that access the shared memory before the critical code sequence completes full execution (i.e., before the sequence completes execution from start to end), the state or contents of the shared memory might be unreliable upon return to execution of the critical code at the point of interruption since other processes or code that may have executed during the interruption may have caused interference to the shared memory. This is one example of interference caused by an interruption.
Software and computer system developers have created a number of conventional techniques to allow a sequence of critical code instructions in a process to execute in an atomic manner to ensure that interference caused by interruptions is avoided. One such conventional technique is an atomic instruction used within software code called a xe2x80x9ccompare and swapxe2x80x9d (CAS) instruction. Generally, a CAS instruction provides a technique for depositing a value into a memory location while guaranteeing that processing leading up to the CAS instruction is not interrupted.
In operation, prior to execution of the CAS instruction, a processor executes a load instruction to fetch a value from a known memory location M. This memory location M is typically the target memory location to which data must be written to in an atomic manner (i.e., without interference). Then, a processor executes one or more critical code instructions in the code to perform any required critical code processing. Finally, the processor executes the CAS instruction typically as the last instruction at the end of the critical section of code. The CAS instruction receives a set of parameters including an old value, a new value, and an address of the memory location M. The CAS instruction obtains ownership of the shared memory or cache at the location M specified by the address parameter and then obtains the value of data stored at this location. The CAS instruction then compares the value obtained from location M with the old value parameter provided to the CAS instruction. If the old value (i.e., the parameter) equals the value obtained from the location of the address M (i.e., the value placed there are the beginning of the critical code section), then the CAS instruction can assume that no interference has taken place to this memory location and the CAS instruction proceeds to store the new value at that location M. The CAS instruction also returns the new value as output. In the alternative, if the old value parameter does not equal the value that the CAS instructions retrieves from the location of the address M, then the CAS instruction can infer that some processing has disturbed or caused interference to the original value at memory location M. In such cases, the CAS instruction does not write to memory, but does return the value fetched from location M. Upon such an indication, the processor can re-execute the critical code by jumping to the start of the critical code (i.e., by jumping back to the initial store instruction) in another attempt to execute the critical code form start to end without interference.
A typical conventional process uses the CAS instruction at the end of critical code to form a loop that continually attempts to successfully execute the critical code ending with the CAS instruction each time this instruction fails. In this manner, a process operating the CAS instruction will not continue execution beyond the critical code section until the CAS instruction is able to be successfully completed one time, thus guaranteeing that all of the critical code has been completely performed and the new value is placed into the memory location specified by the address parameter without interference from any interruptions that may have occurred during execution of all critical code preceding the CAS instruction (beginning with the original or old value being loaded from the memory location that the CAS instruction eventually checks).
An example of the CAS instruction is shown in the following code segment:
RETRY:
LD Mxe2x86x92TMP;
TMP+1xe2x86x92TMP2; (interruption causing interference might occur here)
CAS M,TMP,TMP2;
IF TMP!=TMP2 GOTO RETRY;
As shown in the example above, a processor executes the LD instruction to load the contents of memory location M into the TMP variable. Next, a sequence of one or more instructions are executed to manipulate data. In this example the variable TMP2 is set to the value of TMP+1. During this processing, an interruption causing interference might occur causing a change to the memory location M. After processing all instructions that require atomic execution has been completed, the CAS instruction stores the contents of TMP2 into memory location M if and only if TMP and M are the same. After the CAS instruction, a test is done to check to determine if TMP and TMP2 are the same. If they are, the CAS instruction executed successfully and atomically. If not, then this processing repeats until the CAS instruction is successfully completed. The CAS instruction might fail, for instance, if another processor operates a process which accesses data at the memory location M thus causing interference, or if an interrupt occurred between the LD and the CAS, and another thread executed on the processor in the interim, that thread may have modified location M, rendering the values in TMP and TMP2 registers xe2x80x9cstalexe2x80x9d (i.e., out of date with respect to memory).
Another conventional technique that provides for atomic execution of critical code sections is called a xe2x80x9cload linked store conditionalxe2x80x9d or LL/SC technique. Generally, the load linked store conditional technique involves the use of two processor instructions: a load linked (LL) instruction followed by a store conditional (SC) instruction. The two instructions operate much like conventional load and store instructions except that the LL instruction, in addition to doing a simple load, has a side effect of setting a user transparent bit called a load link bit. The load link bit forms a xe2x80x9cbreakable linkxe2x80x9d between the LL instruction and a subsequently executed SC instruction. The SC instruction performs a simple store to memory if and only if the load link bit is set when the SC instruction is executed. If the load link bit is not set, then the store will fail to execute. The success or failure of the SC instruction is indicated in a register after the execution of the SC instruction. For example, the processor may load such a register with a xe2x80x9c1xe2x80x9d in case of a successful store or may load the register with a xe2x80x9c0xe2x80x9d if the store was unsuccessful due to the load link bit being reset. The load link bit may be reset by hardware or software (i.e., changed from the state induced from the original LL instruction) upon occurrence of events that have the potential to modify the memory location from which the LL originally loaded data, and that occur during execution of the sequence of code between the LL instruction and the SC. In other words, a section of critical code that must be executed atomically can be inserted between the LL and SC instructions and the SC instruction will only store data to a specified memory location (i.e., the data being modified by the atomic code instructions) if the load link bit is not reset.
An example of where a link can be broken between an LL and SC instruction on a multiprocessor system is when an xe2x80x9cinvalidatexe2x80x9d occurs to a cache line of shared memory which is the subject of the LL. In other words, the link might be broken between the LL and the SC instructions if the processor that executes the LL observes an external update to the cache line, or if an intervention of snoop operation invalidates the line associated with the bit. The link may also be broken by the completion of a return from an exception (i.e., interrupt). It may be the case, for example, that an interrupt to the critical code occurs after execution of the LL instruction but before the SC instruction. During the interrupt, some other processor may have successfully completed a store operation to that same shared data which causes the load link bit to be reset. To avoid interference, the software or hardware will explicitly break the link when returning from the operating system back into the interrupted critical code. This will result in the subsequent SC failing.
An example of pseudocode that illustrates the load linked store conditional technique is as follows (with the text in parenthesis indicating the nature of the processing performed):
RETRY:
LL Mxe2x86x92TMP; (load link bit set)
TMP+1xe2x86x92TMP2; (interruption causing interference and resetting the load link bit might occur here)
SC TMP2, M; (only store if load link bit still set)
IF FAILED_BIT=1GOTO RETRY;
As shown in the example above, the processor executes the LL instruction that operates to load the contents of memory location M into the TMP variable. The LL instruction further sets the load link bit. Next, a sequence of one or more instructions are executed to manipulate data. In this example the variable TMP2 is set to the value of TMP+1. During this processing, an interruption causing interference might occur causing the load link bit to be reset. After processing all instructions that require atomic execution is complete, the SC instruction stores the contents of TMP2 into memory location M if and only if load link bit set by the LL instruction is still set (i.e., is not reset). After the SC instruction, a test is done to check a failure bit (FAILED_BIT) in a processor status register associated with the processor executing this critical code to determine if the SC instruction executed successfully. If the FAILED_BIT equals 1, processing returns to the RETRY location in order to again attempt to execute this section of critical code. This processing repeats until the SC is successfully completed. The SC instruction might fail, for instance, if another processor operates a process which accesses data at the memory location M, thus causing the load link bit to be reset (i.e., thus causing interference).
Another conventional technique used to ensure atomic execution of critical code instructions is referred to as a lock/unlock technique. The lock/unlock technique can be used, for example, in situations where a portion of shared user level code must be executed atomically. When a user level process enters a section of shared critical code, the first instruction that is executed is a lock instruction which attempts to gain ownership of and set a flag indicating a user level process is in the process of executing this section of critical code. When the user level process succeeds in owning and setting this flag, the process can then execute the remainder of the critical code with or without interruption(s). When the process has completed execution of the critical code instructions, the final instruction the process executes to complete the critical code is an unlock instruction which clears the lock flag thus allowing another user level process to gain ownership of the lock flag and to execute this section of shared critical code. No process is allowed to execute this section of shared critical code until it owns the lock flag. If a user level process is interrupted during execution of a critical section of code, that user level process continues to xe2x80x9cownxe2x80x9d the lock on that section of code and other user level processes (as well as the interrupted process) are blocked from executing that section of code until the interrupt has been handled and processing returns to complete execution of the shared critical code by the user level process that owns the lock on the critical code. That process then completes execution of that section of critical code after the interrupt and performs the unlock instruction to free that critical section of code for ownership and execution by another user level process. Since no other processes could execute the critical code section during the interrupt, it is assumed that interference did not occur.
Conventional techniques for ensuring the correct atomic operation of critical code in a computer system without interference (due to interruptions, the action of other processors, or other causes) suffer from certain deficiencies. In particular, conventional critical section execution techniques such as compare and swap and load linked store conditional perform testing at the end of a section of critical code to determine if an interruption causing interference occurred sometime during execution of the critical code. If the critical code section contains many instructions (i.e., is lengthy), then an interruption or remote action that causes interference to the critical code near the start may not be detected until the end of the complete execution of the critical code section at which point the compare and swap or load linked store conditional are performed. In other words, such techniques may involve the processing of unnecessary critical code instructions prior to the detection of the interference and then subsequent re-execution of the critical code section from the beginning. Any critical code instructions which are executed after the occurrence of the interruption causing interference but prior to the operation of the compare and swap or load linked store conditional instruction are simply overhead instructions which consume valuable processing cycles.
In addition, techniques such as compare and swap and load linked store conditional cause re-execution of the critical code from a point that corresponds to the beginning of the critical code section. Accordingly, during execution of a critical code section containing numerous instructions, an interruption that causes interference that occurs towards the end of the execution of such instructions (i.e., thus many critical code instructions were executed prior to the interference from interruption) requires that all critical code instructions be re-executed again, even though some of such critical code instructions were properly executed the first time without interference.
Further still, conventional critical code execution techniques that guarantee atomic execution of the critical code provide no ability to detect interference immediately upon return from an interruption. Accordingly, the drawbacks mentioned above of unnecessarily executing critical code instructions prior to detecting interference cause unnecessary processor overhead.
The lock/unlock technique for ensuring atomic execution of critical code introduces another problem called the xe2x80x9cconvoyxe2x80x9d problem. In the lock/unlock technique, once a first user process obtains ownership and sets the lock flag and begins execution of a critical code section, other user processes are unable to enter the critical code section and remain blocked until completion of the critical code section by the first user process. This can be problematic since the first user process might be interrupted for a prolonged period of time during execution of the critical code. Such an interrupt, whether or not causing interference, can impose a significant delay on the execution of the first user process as well as the other user processes that are blocked from entering the critical code section due to their inability to obtain ownership of the lock flag. In other words, the interruption delay imposed on the first user process is further imposed on other user processes that remain blocked while awaiting ownership of the critical code. Accordingly, those other user processes are xe2x80x9cconvoyedxe2x80x9d until the first user process interruption is complete and the first user process completes execution of the critical code section and unlocks the lock flag.
In addition, lock/unlock techniques can also suffer from a problem known as the priority inversion effect. In such situations, a low priority process might obtain a lock on a portion of critical code. This low priority process might then be preempted by the operating system for a higher priority process. During execution, the higher priority process might require access to the critical code, but cannot do so due to the lock on this code still existing from the lower priority process. In such cases, the higher priority process is impeded by the lower priority process due to the lock.
These and other constraints of conventional techniques for atomically executing critical code sections can significantly increase the amount of processing time spent by a process in an attempt to fully execute a critical code section while avoiding the effects of interference caused by interruptions.
Embodiments of the invention provide the ability to execute critical code sections in an atomic manner without interference. Certain embodiments can significantly avoid certain drawbacks discussed above with respect to conventional techniques for atomically executing of critical code. One embodiment of the invention operates in a computerized device to register a critical code section (or a thread or process containing critical code) with an operating system for invocation of a critical execution manager in the event of an interruption to the critical code. The registration process can cause the operating system to store the address bounds of the critical code in a data structure for use by the critical execution manager upon returning from handling an interruption to execution of the critical code. Registration can also xe2x80x9cregisterxe2x80x9d the critical execution manager as code that is to be invoked upon returning from an interruption to the thread containing the critical code.
In one implementation, for registration to take place, a hook into the operating system can be exploited to indicate to the operating system that the critical execution manager is the code that is to be executed upon returning from all interruptions to the thread containing the critical code. Generally, the process containing the critical code informs the operating system of its intent to use the invention prior to entering critical code by registering a xe2x80x9ccall-backxe2x80x9d address of code containing the critical execution manager with the kernel.
A processor then executes the thread and the thread may alternate between executing normal code and critical code. If an interrupt occurs during normal code execution, no special action is taken and the interrupt is handled normally. However, if the interrupt occurs while in the critical code, since registration is done just prior to entry into the critical code, upon completion of interrupt handling, processing returns to the call back address of the critical execution manager. In other words, once the operating system handles the interrupt, the operating system invokes execution of the critical execution manager configured according to embodiments of this invention. The critical execution manager is able to detect if the interrupt occurred in the critical code and can detect if an interference signal indicates a reset value. In other words, the critical execution manager is able to detect whether or not the interruption could potentially cause interference to critical code based upon a value of an interference signal. According to different embodiments of the invention, the interference signal can be set or can otherwise indicate the reset value in a number of ways. In a conservative implementation, the interference signal can be set during execution of the critical code to indicate that a reset operation is required on the critical code in the event of any interruption to the execution of the critical code, whether or not that interruption actually causes interference to information (e.g., processor or memory information) related to the critical code.
If, after returning from the interruption, the interference signal indicates a reset value, the critical execution manager performs a reset operation on the critical code to reset the current state of the critical code to avoid interference from handling the exception or interruption. The critical execution manager then returns processing to execution of the critical code using the current state of the critical code. Embodiments of the invention can determine if the interference signal indicates a reset value, for example, by detecting an interference signal set as a result of execution of the critical code, or by determining that a second thread was executed by a processor associated with the first thread (i.e., the thread containing the critical code) during handling of the interruption of the first thread, or by detecting that the thread containing the critical code has been migrated to a processor that is different that a processor upon which the thread was executing at a time of the interruption. Generally, the two preconditions that the critical execution manager can detect to indicate a reset value is present is that an interruption (i.e., preemption) or migration of a process occurred and that execution was taking place in the critical section at that time.
Once the critical execution manager determines that the interference signal indicates a reset value, the reset operation can be performed in a variety of ways according to different embodiments of the invention. Generally, the operation of performing the reset operation includes replacing current processor information (e.g., a current program counter) existing within the current state of the critical code with processor information obtained from the registered or saved state of the critical code (or of the registered thread) in order to allow the operation of returning to execution of the critical code to begin execution (e.g., restart) of the critical code without interference from handling the interruption. In this manner, the critical execution manager can use the registered state of the critical code saved prior to handling interruption to update the current state of the critical code that exists upon returning to the critical code after handling interruption.
According to embodiments of the invention, the state of critical code (i.e., the save state and/or a current state) can include information including processor information related to the critical code such as register values, flag settings or other processor specific information. The state of critical code can also include memory information that is related to the critical code such as addresses and/or values of memory locations referenced by instructions within the critical code.
In some embodiments of the invention, the operation of detecting if an interference signal indicates a reset value can include detecting that a thread containing the critical code has been migrated to a processor that is different than a processor upon which that thread was executing at the time of the interruption. In such a case, the reset operation can include receiving (e.g., as part of the interference signal) an identification of a processor upon which the critical code is scheduled to execute next and then adjusting memory references of offsets that are associated with the critical code for the processor upon which the critical code is scheduled to execute next in order to allow the critical code to properly reference processor specific data during operation of returning to execution of the critical code on the new processor. Accordingly, if after interruption the critical code is now migrated to another processor, the memory information associated with a new processor can be properly accessed by the critical code using the processor identification to computer address offsets into processor specific memory thus allowing the critical code to continue to operate properly.
Once embodiments detect that the interference signal indicates a reset value, embodiments of the invention can operate in a variety of unique ways to resume execution of the critical code. In particular, some embodiments of the invention are able to perform a xe2x80x9croll-backxe2x80x9d or abort operation as part of the reset operation in order to restart the critical code from its beginning while in ensuring that the current state of the critical code is reset to a state that existed prior to occurrence of the interruption. This roll-back operation allows the operation of returning to execution of the critical code to begin execution of the critical code without interference from handling interruption. In particular, in the xe2x80x9croll-backxe2x80x9d embodiments of the invention, the registered state of the critical code identifies the start of execution of the beginning of the critical code. Also in the roll-back embodiments, the reset operation (e.g., modifying current processor information existing within the current state of the critical code with registered thread information) includes resetting a program counter for the critical code to reference an instruction existing at the beginning of the critical code in response to the interruption. In doing so, upon returning to execution of the critical code, such execution begins at the start or beginning of the critical code and the current state of the code is exactly as it was upon initial entry into the critical code the first time it was executed.
Other embodiments of the invention can utilize a roll-forward operation to avoid interference caused by interruptions. In roll-forward embodiments of the invention, the operating system kernel inherently save a state of the critical code in response to the interruption to the execution of the critical code. Then, after handling the interruption and detecting that the interference signal indicates a reset value, the critical execution manager can restore, modify or reset a program counter for the critical code to reference an instruction that was interrupted in the critical code by referencing the kernel information indicating this state. This embodiment can also reload register information associated with the critical code from the registered state of the critical code. The register information can contain information related to the critical code at the time of the interruption. In a critical section of code, upon returning from an interruption, intermediate values in registers may be xe2x80x9cstale.xe2x80x9d In one embodiment, the critical execution manager causes execution to resume at a special location that operates as a repair entry point that contains code to refresh the stale register values and then transfer control back an appropriate location in the critical code, which may be the interrupted location (roll-forward) or the beginning of the critical section (roll-back).
In the roll-forward embodiments, when returning to execution of the critical code after the interruption and after the reset operation, such execution begins at the instruction that was interrupted in the critical code as now referenced by the program counter for the critical code. In other words, this embodiment performs a roll-forward operation that causes the critical code to resume execution at the point of interruption instead of returning to the beginning of the critical code. This avoids having to re-execute critical code instructions that previously executed prior to the interruption that caused the interference.
Other embodiments of the invention are also able to perform the reset operation using one or more compensation transactions. Compensation transactions generally allow such embodiments to undo or reverse changes made to processor information such as register values related to the critical code that may have been modified by the interruption. Alternatively, compensation transactions can undo changes made to variables or registers by the critical code prior to the interruption point, but after commencing execution of the code. In other words, compensation transactions can either undo interference by compensating for such interference or in an alternative configuration, can undo operations performed by the prior execution of the critical code up to the point of interruption. As an example, if another processor accesses shared memory or registers referenced by the critical code during the interruption, the compensation transactions can detect this change and can reverse or undo this change in order to restore the processor or memory information to a state that existed prior to the interruption thus compensating for the interruption. This is useful in roll-forward embodiments to reestablish a register state as it existed at the beginning of the critical section so that when the program counter is set to reference the beginning of the critical code, the execution of the critical code can being having a complete state that is unmodified due to interference. In an alternative arrangement of the use of compensation transactions such as may be employed in roll-back embodiments, if some instructions in the critical code execute prior to an interruption to add ten to a variable, the compensation instructions might subtract ten from this variable value prior to returning to execution at the beginning of the critical code (in a roll-back embodiment) to thus re-establish the state of the critical code section as it appeared at its initial start. Conventional techniques for returning to execution of the critical code can fail in this regard as they simply return to execution of the start of the critical code but do nothing to prevent, reverse, undo or compensate for modification of register contents or other contents such as memory locations that may have occurred during interruption.
According to one embodiment of the invention, the techniques above apply to user level processes or threads that operate on processor specific data. In other words, the critical code is associated with a user thread that is executing on a specific processor within a multi-processing computerized device and further more, the critical code will be accessing data that is specifically and uniquely associated with the processor (e.g., processor specific data). In addition, the techniques of the invention do not require locking mechanisms (i.e., are lock-free or mutual exclusion free) and thus avoid issues such as the convoy problem associated with conventional techniques for executing critical code.
Embodiments of the invention can be implemented as methods in a process or as circuitry or hardware or as software or a combination of hardware and software within a computerized device or as software encoded within a computer readable medium. In particular, other embodiments of the invention include a computer system, such as a computerized device, workstation, handheld or laptop computer, or other device configured with software and/or circuitry to process all of the method operations disclosed herein as embodiments of the invention. In such embodiments, the computerized device is capable of executing critical code without interference and includes a processor and a memory encoded with an operating system, critical code, and a critical execution manager. An interconnection mechanism couples the processor and the memory and the processor executes critical code, the critical execution manager and the operating system causing the processor to perform the operations of embodiments of the invention as explained herein. In other words, a computerized device such as a computer that is programmed to operate as explained herein is considered an embodiment of the invention.
Other arrangements of embodiments of the invention that are disclosed herein include software programs to perform the method embodiment steps and operations summarized above and disclosed in detail below. As an example, an operating system configured with a critical execution manager that operates as explained herein is considered an embodiment of the invention. Alternatively, the critical execution manager may be implemented in a user process, or partly in an operating system and partly in one or more user space processes, or in any combination thereof. More particularly, a computer program product is disclosed which has a computer-readable medium including computer program logic encoded thereon that, when executed on at least one processor with a computing system or computerized device, causes the processor to perform the operations (e.g., the methods and steps) disclosed herein as embodiments of the invention. Such arrangements of the invention are typically provided as software, code and/or other data (e.g., data structures) arranged or encoded on a computer readable medium such as an optical medium (e.g., CD-ROM), floppy or hard disk or other a medium such as firmware or microcode in one or more ROM or RAM or PROM chips or as an Application Specific Integrated Circuit (ASIC). The software or firmware or other such configurations can be installed onto a computer system to cause the computer system to perform the techniques explained herein as embodiments of the invention.
It is to be understood that the system of the invention can be embodied strictly as a software program, as software and hardware, or as hardware alone. Example embodiments of the invention may be implemented within computer systems, computer program products, operating systems and software applications manufactured by Sun Microsystems of Palo Alto, California, USA.